/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */
package cn.ac.nercis.pes.service.document.common;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.constant.common.ReportConstants;
import cn.ac.nercis.pes.common.constant.knowledge.AssetClassification;
import cn.ac.nercis.pes.common.constant.knowledge.EvaluationMethod;
import cn.ac.nercis.pes.common.constant.knowledge.TargetDeepness;
import cn.ac.nercis.pes.common.constant.knowledge.TargetType;
import cn.ac.nercis.pes.model.bo.doc.common.table.target.BaseTargetBO;
import cn.ac.nercis.pes.model.bo.doc.common.table.target.NotUseTargetBO;
import cn.ac.nercis.pes.model.bo.doc.common.table.target.SpecialTargetBO;
import cn.ac.nercis.pes.model.bo.doc.report.evaluation.ObjectConfirmBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.TargetTableReportBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.evaluation.EvaluationMethodReportBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.evaluation.ManageEvaluationDataReportBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.evaluation.TecEvaluationDataReportBO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationDescRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemSpecialTargetRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.project.SystemSpecialTargetDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.utils.AssetUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 报告系统指标数据服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record DocTargetDataService(SystemTargetRepository systemTargetRepository,
                                   SystemSpecialTargetRepository systemSpecialTargetRepository,
                                   AssetRepository assetRepository,
                                   AssetEvaluationMethodRepository evaluationMethodRepository,
                                   AssetEvaluationDescRepository evaluationDescRepository) {
    @Autowired
    public DocTargetDataService {
    }

    public Long getBaseTargetCount(@NonNull SystemDO systemDO) {
        try {
            List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemDO.getId());
            if (CollectionUtils.isEmpty(systemTargets)) {
                return 0L;
            }
            return getBaseTargetCount(TargetType.TECHNOLOGY, systemTargets)
                    + getBaseTargetCount(TargetType.MANAGEMENT, systemTargets);
        } catch (RuntimeException rEx) {
            log.error("{}/getBaseTargetCount 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public TargetTableReportBO<BaseTargetBO> getBaseTarget(@NonNull SystemDO systemDO) {
        try {
            List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemDO.getId());
            if (CollectionUtils.isEmpty(systemTargets)) {
                return baseTargetByEmpty();
            }
            TargetTableReportBO<BaseTargetBO> data = new TargetTableReportBO<>();
            List<BaseTargetBO> contents = new ArrayList<>();
            contents.addAll(getBaseTarget(TargetType.TECHNOLOGY, systemTargets));
            contents.addAll(getBaseTarget(TargetType.MANAGEMENT, systemTargets));
            data.setSum(contents.size());
            data.setContents(contents);
            return data;
        } catch (RuntimeException rEx) {
            log.error("{}/getBaseTarget 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public TargetTableReportBO<SpecialTargetBO> getSpecialTarget(@NonNull SystemDO systemDO){
        try{
            var specialTargets = this.systemSpecialTargetRepository.findAllBySystemId(systemDO.getId());
            var systemTargets = this.systemTargetRepository.findAllBySystemId(systemDO.getId());
            if(CollectionUtils.isEmpty(specialTargets) || CollectionUtils.isEmpty(systemTargets)){
                return notSpecialTargetByEmpty();
            }
            TargetTableReportBO<SpecialTargetBO> data = new TargetTableReportBO<>();
            var contents = systemTargets.stream()
                    .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                    .flatMap(st -> getSpecialTarget(st, specialTargets).stream())
                    .collect(Collectors.toList());
            data.setSum(contents.size());
            data.setContents(contents);
            return data;
        } catch (RuntimeException rEx) {
            log.error("{}/getSpecialTarget 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public TargetTableReportBO<NotUseTargetBO> getNotUseTarget(@NonNull SystemDO systemDO) {
        try {
            List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemDO.getId());
            if (CollectionUtils.isEmpty(systemTargets)) {
                return notUseTargetByEmpty();
            }
            TargetTableReportBO<NotUseTargetBO> data = new TargetTableReportBO<>();
            var contents = systemTargets.stream()
                    .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                    .flatMap(st -> getNotUseTarget(st, systemTargets).stream())
                    .collect(Collectors.toList());
            data.setSum(contents.size());
            data.setContents(contents);
            return data;
        } catch (RuntimeException rEx) {
            log.error("{}/getNotUseTarget 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public Long getNotUseTargetCount(@NonNull SystemDO systemDO) {
        try {
            List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemDO.getId());
            if (CollectionUtils.isEmpty(systemTargets)) {
                return 0L;
            }
            return systemTargets.stream()
                    .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                    .mapToLong(st -> getNotUseTarget(st, systemTargets).size())
                    .sum();
        } catch (RuntimeException rEx) {
            log.error("{}/getNotUseTargetCount 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public List<String> getNotUseTargetNames(@NonNull SystemDO systemDO) {
        try {
            List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemDO.getId());
            if (CollectionUtils.isEmpty(systemTargets)) {
                return Collections.emptyList();
            }
            return systemTargets.stream()
                    .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                    .flatMap(st -> getNotUseTargetName(st, systemTargets).stream())
                    .toList();
        } catch (RuntimeException rEx) {
            log.error("{}/getNotUseTargetNames 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public ObjectConfirmBO getAssetEvaluationMethod(@NonNull SystemDO systemDO) {
        try {
            List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemDO.getId());
            if (CollectionUtils.isEmpty(systemTargets)) {
                return confirmByEmpty();
            }
            List<SystemTargetDO> layerTargets = systemTargets.stream()
                    .filter(st -> st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                    .collect(Collectors.toList());
            SystemTargetDO phyEnvTarget = layerTargets.stream()
                    .filter(lt -> StringUtils.equals(lt.getKmName(), ReportConstants.OBJECT_CONFIG_PHY_ENV_TARGET_NAME))
                    .findFirst()
                    .orElse(null);
            SystemTargetDO netCommunicationTarget = layerTargets.stream()
                    .filter(lt -> StringUtils.equals(lt.getKmName(), ReportConstants.OBJECT_CONFIG_NET_COMMUNICATION_TARGET_NAME))
                    .findFirst()
                    .orElse(null);
            SystemTargetDO deviceComputeTarget = layerTargets.stream()
                    .filter(lt -> StringUtils.equals(lt.getKmName(), ReportConstants.OBJECT_CONFIG_DEVICE_COMPUTE_TARGET_NAME))
                    .findFirst()
                    .orElse(null);
            SystemTargetDO appDataTarget = layerTargets.stream()
                    .filter(lt -> StringUtils.equals(lt.getKmName(), ReportConstants.OBJECT_CONFIG_APP_DATA_TARGET_NAME))
                    .findFirst()
                    .orElse(null);
            var assets = this.assetRepository.findAllBySystemId(systemDO.getId());
            return ObjectConfirmBO.builder()
                    .phyEnv(getTechnologyEvaluationMethod(systemDO.getId(), phyEnvTarget, assets))
                    .netCommunication(getTechnologyEvaluationMethod(systemDO.getId(), netCommunicationTarget, assets))
                    .deviceCompute(getTechnologyEvaluationMethod(systemDO.getId(), deviceComputeTarget, assets))
                    .appData(getTechnologyEvaluationMethod(systemDO.getId(), appDataTarget, assets))
                    .securityManage(getManageEvaluationMethod(systemDO.getId(), layerTargets))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getAssetEvaluationMethod 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    private List<TecEvaluationDataReportBO> getTechnologyEvaluationMethod(String systemId, SystemTargetDO layerTarget, List<AssetDO> assets) {
        if (Objects.isNull(layerTarget)) {
            return evaluationTableByEmpty();
        }
        var evaluationData = evaluationMethodRepository.findAllBySystemIdAndLayerTargetId(systemId, layerTarget.getKmTargetId())
                .stream()
                .sorted(Comparator.comparingInt(AssetEvaluationMethodDO::getUnitTargetSort))
                .toList();
        var layerEvalDescData = evaluationDescRepository.findAllBySystemIdAndLayerTargetId(systemId,layerTarget.getKmTargetId());
        //获取层面包含的资产对象ID
        Set<String> assetIds = evaluationData.stream().map(AssetEvaluationMethodDO::getAssetId).collect(Collectors.toSet());
        var relatedAssets = assets.stream()
                .filter(a -> assetIds.stream().anyMatch(id -> StringUtils.equals(a.getId(), id)))
                .sorted(Comparator.comparing((a) -> Objects.nonNull(a.getQuoteAssetId()) ? a.getQuoteAssetId() : a.getClassId()))
                .toList();
        List<TecEvaluationDataReportBO> contents = new ArrayList<>();
        int number = 1;
        for (var item : relatedAssets) {
            var assetName = AssetUtils.getAssetName(item);
            var methodDO = evaluationData.stream()
                    .filter(aem -> StringUtils.equals(aem.getAssetId(), item.getId()))
                    .findFirst()
                    .orElse(null);
            var evalDesc = layerEvalDescData.stream()
                    .filter(aed->StringUtils.equals(aed.getAssetId(), item.getId()))
                    .findFirst()
                    .orElse(null);
            //额外排除《重要数据》资产对象测评方式
            if (StringUtils.isBlank(assetName) || Objects.isNull(methodDO)
                    || StringUtils.equals(methodDO.getClassId(), AssetClassification.KEY_DATA.getId())
                    || StringUtils.equals(methodDO.getClassId(), AssetClassification.KEY_BUSINESS.getId())) {
                continue;
            }
            if(Objects.nonNull(evalDesc)){
                contents.add(TecEvaluationDataReportBO.builder()
                        .number(number)
                        .name(assetName)
                        .methods(parseTechnologyEvaluationMethod(evalDesc.getMethod()))
                        .desc(evalDesc.getDesc())
                        .build());
            }else{
                contents.add(TecEvaluationDataReportBO.builder()
                        .number(number)
                        .name(assetName)
                        .methods(parseTechnologyEvaluationMethod(methodDO.getMethod()))
                        .desc(methodDO.getDesc())
                        .build());
            }
            number++;
        }
        if (CollectionUtils.isEmpty(contents)) {
            return evaluationTableByEmpty();
        }
        return contents;
    }

    private List<ManageEvaluationDataReportBO> getManageEvaluationMethod(String systemId, List<SystemTargetDO> layerTargets) {
        List<SystemTargetDO> manageTargets = layerTargets.stream()
                .filter(lt -> lt.getKmType() == TargetType.MANAGEMENT)
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .toList();
        List<ManageEvaluationDataReportBO> contents = new ArrayList<>();
        AtomicInteger number = new AtomicInteger(1);
        for (SystemTargetDO lt : manageTargets) {
            contents.addAll(getManageEvaluationMethod(number, systemId, lt));
        }
        if (CollectionUtils.isEmpty(contents)) {
            return manageEvaluationByEmpty();
        }
        return contents;
    }

    private List<ManageEvaluationDataReportBO> getManageEvaluationMethod(AtomicInteger number, String systemId, SystemTargetDO layerTarget) {
        List<AssetEvaluationMethodDO> evaluationData = evaluationMethodRepository.findAllBySystemIdAndLayerTargetId(systemId, layerTarget.getKmTargetId())
                .stream()
                .sorted(Comparator.comparingInt(AssetEvaluationMethodDO::getUnitTargetSort))
                .toList();
        //获取层面包含的资产对象ID
        Set<String> assetIds = evaluationData.stream().map(AssetEvaluationMethodDO::getAssetId).collect(Collectors.toSet());
        List<ManageEvaluationDataReportBO> contents = new ArrayList<>();
        for (String assetId : assetIds) {
            String assetName = getAssetName(assetId);
            AssetEvaluationMethodDO methodDO = evaluationData.stream().filter(aem -> StringUtils.equals(aem.getAssetId(), assetId)).findFirst().orElse(null);
            if (StringUtils.isBlank(assetName) || Objects.isNull(methodDO)) {
                continue;
            }
            contents.add(ManageEvaluationDataReportBO.builder()
                    .number(number.getAndIncrement())
                    .unit(layerTarget.getKmName())
                    .name(assetName)
                    .methods(parseManageEvaluationMethod(methodDO.getMethod()))
                    .desc(methodDO.getDesc())
                    .build());
        }
        return contents;
    }

    private List<EvaluationMethodReportBO> parseTechnologyEvaluationMethod(String method) {
        if (StringUtils.isBlank(method)) {
            return EvaluationMethod.getTechnologyEvaluationMethods()
                    .stream()
                    .map(em -> new EvaluationMethodReportBO(em.getName(), false))
                    .collect(Collectors.toList());
        }
        List<String> selected = Arrays.asList(StringUtils.split(method, BaseConstants.MULTI_SPLIT_CHAR));
        return EvaluationMethod.getTechnologyEvaluationMethods()
                .stream()
                .map(em -> {
                    boolean checked = selected.stream()
                            .anyMatch(value -> StringUtils.equals(value, String.valueOf(em.getMethod())));
                    return new EvaluationMethodReportBO(em.getName(), checked);
                })
                .collect(Collectors.toList());
    }

    private List<EvaluationMethodReportBO> parseManageEvaluationMethod(String method) {
        if (StringUtils.isBlank(method)) {
            return EvaluationMethod.getManageEvaluationMethods()
                    .stream()
                    .map(em -> new EvaluationMethodReportBO(em.getName(), false))
                    .collect(Collectors.toList());
        }
        List<String> selected = Arrays.asList(StringUtils.split(method, BaseConstants.MULTI_SPLIT_CHAR));
        return EvaluationMethod.getManageEvaluationMethods()
                .stream()
                .map(em -> {
                    boolean checked = selected.stream()
                            .anyMatch(value -> StringUtils.equals(value, String.valueOf(em.getMethod())));
                    return new EvaluationMethodReportBO(em.getName(), checked);
                })
                .collect(Collectors.toList());
    }

    private String getAssetName(String assetId) {
        return this.assetRepository.findByIdAndIsUse(assetId, true)
                .map(AssetUtils::getAssetName)
                .orElse("");
    }

    private List<BaseTargetBO> getBaseTarget(TargetType targetType, List<SystemTargetDO> systemTargets) {
        return systemTargets.stream()
                .filter(st -> st.getKmType() == targetType
                        && st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .flatMap(st -> getBaseTarget(st, systemTargets).stream())
                .collect(Collectors.toList());
    }

    private Long getBaseTargetCount(TargetType targetType, List<SystemTargetDO> systemTargets) {
        return systemTargets.stream()
                .filter(st -> st.getKmType() == targetType
                        && st.getKmDeepness() == TargetDeepness.LAYER.getDeepness())
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .mapToLong(st -> getBaseTarget(st, systemTargets).size())
                .sum();
    }

    private List<BaseTargetBO> getBaseTarget(SystemTargetDO layerTarget, List<SystemTargetDO> systemTargets) {
        return systemTargets.stream()
                .filter(st -> StringUtils.equals(st.getKmParentId(), layerTarget.getKmTargetId()))
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .map(st -> BaseTargetBO.builder()
                        .type(st.getKmType().getName())
                        .layer(layerTarget.getKmName())
                        .unit(st.getKmName())
                        .target(getTargetNameByParentId(st.getKmTargetId(), systemTargets))
                        .require(st.getKmApplyClaim())
                        .build())
                .collect(Collectors.toList());
    }

    private List<SpecialTargetBO> getSpecialTarget(SystemTargetDO layerTarget, List<SystemSpecialTargetDO> specialTargets) {
        return specialTargets.stream()
                .filter(st -> StringUtils.equals(st.getLayerTargetId(), layerTarget.getKmTargetId()))
                .sorted(Comparator.comparingInt(SystemSpecialTargetDO::getSort))
                .map(st -> SpecialTargetBO.builder()
                        .layer(layerTarget.getKmName())
                        .unit(st.getName())
                        .target(st.getTargetDesc())
                        .require(st.getKmApplyClaim())
                        .reason(st.getReasons())
                        .build())
                .collect(Collectors.toList());
    }

    private List<NotUseTargetBO> getNotUseTarget(SystemTargetDO layerTarget, List<SystemTargetDO> systemTargets) {
        return systemTargets.stream()
                .filter(st -> StringUtils.equals(st.getKmParentId(), layerTarget.getKmTargetId())
                        && (!st.getIsApply()))
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .map(st -> NotUseTargetBO.builder()
                        .layer(layerTarget.getKmName())
                        .unit(st.getKmName())
                        .target(getTargetNameByParentId(st.getKmTargetId(), systemTargets))
                        .reason(st.getReasons())
                        .build())
                .collect(Collectors.toList());
    }

    private List<String> getNotUseTargetName(SystemTargetDO layerTarget, List<SystemTargetDO> systemTargets) {
        return systemTargets.stream()
                .filter(st -> StringUtils.equals(st.getKmParentId(), layerTarget.getKmTargetId())
                        && (!st.getIsApply()))
                .sorted(Comparator.comparingInt(SystemTargetDO::getKmSort))
                .map(st -> String.format("%s/%s", layerTarget.getKmName(), st.getKmName()))
                .collect(Collectors.toList());
    }

    private String getTargetNameByParentId(String parentTargetId, List<SystemTargetDO> systemTargets) {
        return systemTargets.stream()
                .filter(st -> StringUtils.equals(st.getKmParentId(), parentTargetId))
                .map(SystemTargetDO::getKmName)
                .findFirst()
                .orElse("");
    }

    private TargetTableReportBO<BaseTargetBO> baseTargetByEmpty(){
        TargetTableReportBO<BaseTargetBO> data = new TargetTableReportBO<>();
        data.setSum(0);
        data.setContents(List.of(BaseTargetBO.builder()
                .type(" ")
                .layer(" ")
                .unit(" ")
                .target(" ")
                .require(" ")
                .build()));
        return data;
    }

    private TargetTableReportBO<SpecialTargetBO> notSpecialTargetByEmpty() {
        TargetTableReportBO<SpecialTargetBO> data = new TargetTableReportBO<>();
        data.setSum(0);
        data.setContents(List.of(SpecialTargetBO.builder()
                .layer(" ")
                .unit(" ")
                .target(" ")
                .require(" ")
                .reason(" ")
                .build()));
        return data;
    }

    private TargetTableReportBO<NotUseTargetBO> notUseTargetByEmpty() {
        TargetTableReportBO<NotUseTargetBO> data = new TargetTableReportBO<>();
        data.setSum(0);
        data.setContents(List.of(NotUseTargetBO.builder()
                .layer(" ")
                .unit(" ")
                .target(" ")
                .reason(" ")
                .build()));
        return data;
    }

    private ObjectConfirmBO confirmByEmpty() {
        return ObjectConfirmBO.builder()
                .phyEnv(evaluationTableByEmpty())
                .netCommunication(evaluationTableByEmpty())
                .deviceCompute(evaluationTableByEmpty())
                .appData(evaluationTableByEmpty())
                .securityManage(manageEvaluationByEmpty())
                .build();
    }

    private List<TecEvaluationDataReportBO> evaluationTableByEmpty() {
        TecEvaluationDataReportBO data1 = new TecEvaluationDataReportBO();
        data1.setNumber(1);
        data1.setName(" ");
        data1.setMethods(Arrays.
                asList(new EvaluationMethodReportBO("访    谈", false),
                        new EvaluationMethodReportBO("文档审查", false),
                        new EvaluationMethodReportBO("实地查看", false),
                        new EvaluationMethodReportBO("配置检查", false),
                        new EvaluationMethodReportBO("工具测试", false)));
        data1.setDesc(" ");
        return List.of(data1);
    }

    private List<ManageEvaluationDataReportBO> manageEvaluationByEmpty() {
        ManageEvaluationDataReportBO data1 = new ManageEvaluationDataReportBO();
        data1.setNumber(1);
        data1.setUnit(" ");
        data1.setName(" ");
        data1.setMethods(Arrays.
                asList(new EvaluationMethodReportBO("访    谈", false),
                        new EvaluationMethodReportBO("文档审查", false)));
        data1.setDesc(" ");
        return List.of(data1);
    }
}
